import { action, flow, makeObservable, observable, runInAction } from 'mobx';

import { auth } from 'Apis';
import { defaultTenantId } from 'Configs/common';
import LoginStore from 'Core/LoginStore';
import { LoginDataIndex } from 'Models/Login';
import request from 'Utils/request';

import type { AxiosResponse } from 'axios';
import type { ActionRequestCanceler } from 'Typings/common';

class AuthStore extends LoginStore {
    @observable publicKey?: string;

    @observable token?: string;

    @observable getAuthPublicKeyRequesting: boolean;

    @observable getAuthPublicKeyCanceler: ActionRequestCanceler;

    @observable getAuthTokensRequesting?: boolean;

    @observable getAuthTokensCanceler: ActionRequestCanceler;

    @observable deleteAuthTokenRequesting?: boolean;

    @observable deleteAuthTokenCanceler: ActionRequestCanceler;

    constructor() {
        super();
        makeObservable(this);

        this.publicKey = undefined;
        this.token = undefined;

        this.getAuthPublicKeyRequesting = true;
        this.getAuthPublicKeyCanceler = () => {};
        this.getAuthTokensRequesting = true;
        this.getAuthTokensCanceler = () => {};
        this.deleteAuthTokenRequesting = true;
        this.deleteAuthTokenCanceler = () => {};
    }

    @action setToken: (token?: string) => void = value => {
        this.token = value;
    };

    @action destory: NoneParamsAndReturnFuc = () => {
        this.publicKey = undefined;
        this.token = '';

        this.getAuthPublicKeyRequesting = true;
        this.getAuthPublicKeyCanceler = () => {};
        this.getAuthTokensRequesting = undefined;
        this.getAuthTokensCanceler = () => {};
    };

    @flow.bound getAuthPublicKey = function* getAuthPublicKey(this: AuthStore): unknown {
        if (!this || this.publicKey) return;

        this.getAuthPublicKeyRequesting = true;
        try {
            const { apiPostToken } = auth;

            const requestData = {};
            const actionData = yield request(apiPostToken, {
                params: {
                    ...requestData,
                },
                canceler: canceler => {
                    this.getAuthPublicKeyCanceler = canceler;
                },
            });

            if (actionData?.data) {
                const tmpData = actionData.data;

                if (tmpData.pubKey) {
                    const resData = tmpData.pubKey;

                    this.publicKey = resData;
                    yield this.Login();
                }
            }
        } catch (error) {
            runInAction(() => {
                this.getAuthPublicKeyRequesting = false;
            });
        }
    };

    @flow.bound Login = function* Login(this: AuthStore): unknown {
        if (!this) return;

        const {
            [LoginDataIndex.ACCOUNT]: account,
            [LoginDataIndex.PASSWORD]: password,
            // [LoginDataIndex.PHONE]: phone,
            // [LoginDataIndex.CAPTCHA]: captcha,
            [LoginDataIndex.REMEMBER]: remember,
        } = this.createFormData;

        // if (!this || !this.publicKey) return;

        this.getAuthTokensRequesting = true;
        try {
            const { apiPostToken } = auth;

            // const jse = new JSEncrypt();
            // jse.setPublicKey(this.publicKey);
            // const encodedPassword = password ? jse.encrypt(password) : undefined;

            const requestData = {
                grantType: 'password',
                account,
                password,
                refreshToken: false,
                tenantId: defaultTenantId,
            };

            const responseData: AxiosResponse<{
                data?: {
                    accessToken: string;
                    [LoginDataIndex.AUTHORITY]: string;
                };
            }> = yield request(apiPostToken, {
                reqParams: requestData,
                canceler: canceler => {
                    this.getAuthTokensCanceler = canceler;
                },
            });

            if (responseData.data.data?.accessToken) {
                const token = responseData.data.data?.accessToken;
                const authority = responseData.data.data[LoginDataIndex.AUTHORITY];
                this.getAuthTokensRequesting = false;

                yield this.saveTokenToLocalStorage(token);
                yield this.saveAuthorityToLocalStorage(authority);
                if (remember) {
                    yield this.saveInfoToLocalStorage();
                } else {
                    yield this.removeLocalStorageInfo();
                }
                yield this.setToken(token);
            } else {
                throw new Error('no data');
            }
        } catch (error) {
            runInAction(() => {
                this.getAuthTokensRequesting = false;
            });
        }
    };

    @flow.bound Logout = function* deleteAuthToken(this: AuthStore): unknown {
        if (!this) return;

        yield (this.deleteAuthTokenRequesting = true);
        try {
            const { apiPostToken } = auth;

            yield this.removeLocalStorageToken();
            yield this.removeLocalStorageAuthority();
            yield this.setToken(undefined);
            const responseData: AxiosResponse<CommonSysReponse<Record<string, never>>> = yield request(apiPostToken, {
                canceler: canceler => {
                    this.deleteAuthTokenCanceler = canceler;
                },
            });

            if (responseData?.data) {
                this.deleteAuthTokenRequesting = false;
            } else {
                throw new Error('no data');
            }
        } catch (error) {
            runInAction(() => {
                this.deleteAuthTokenRequesting = false;
            });
        }
    };

    @action checkToken = (): boolean => {
        if (!this.token) return false;
        return !!this.token;
    };
}

export default AuthStore;
